﻿namespace Lichtspiel.Math
{
    public struct Vector2
    {
        public float X, Y;

        public float Length {
            get { return (float)System.Math.Sqrt(X * X + Y * Y); }
        }

        public float LengthSquared {
            get { return X * X + Y * Y; }
        }

        public Vector2(float value)
            : this(value, value) { }

        public Vector2(float x, float y) {
            X = x;
            Y = y;
        }

        public Vector2(double x, double y) {
            X = (float) x;
            Y = (float) y;
        }

        public void Normalize() {
            Vector2.Normalize(ref this, out this);
        }

        public Vector2 Normalized() {
            Vector2 v;
            Vector2.Normalize(ref this, out v);
            return v;
        }

        public void Add(ref Vector2 v) {
            this.X += v.X;
            this.Y += v.Y;
        }

        public void Sub(ref Vector2 v) {
            this.X -= v.X;
            this.Y -= v.Y;
        }

        public void Mul(ref Vector2 v) {
            this.X *= v.X;
            this.Y *= v.Y;
        }

        public void Mul(float scale) {
            this.X *= scale;
            this.Y *= scale;
        }

        public void Div(float scale) {
            float mult = 1.0f / scale;
            this.X *= mult;
            this.Y *= mult;
        }

        public override string ToString() {
            return "(" + X + ", " + Y + ")";
        }
        #region operatoren
        public static Vector2 operator +(Vector2 v1, Vector2 v2) {
            return new Vector2(v1.X + v2.X, v1.Y + v2.Y);
        }

        public static Vector2 operator -(Vector2 v1, Vector2 v2) {
            return new Vector2(v1.X - v2.X, v1.Y - v2.Y);
        }


        public static Vector2 operator -(Vector2 v1, Size s) {
            return new Vector2(v1.X - s.Width, v1.Y - s.Height);
        }

        public static Vector2 operator-(Vector2 v) {
            v.X = -v.X;
            v.Y = -v.Y;
            return v;
        }

        public static Vector2 operator *(Vector2 v1, Vector2 v2) {
            return new Vector2(v1.X * v2.X, v1.Y * v2.Y);
        }

        public static Vector2 operator *(Vector2 v1, float f) {
            return new Vector2(v1.X * f, v1.Y * f);
        }

        public static Vector2 operator *(float f, Vector2 v1) {
            return new Vector2(v1.X * f, v1.Y * f);
        }

        public static Vector2 operator /(Vector2 vec, float scale) {
            float mult = 1.0f / scale;
            vec.X *= mult;
            vec.Y *= mult;
            return vec;
        }

        public static bool operator ==(Vector2 left, Vector2 right) {
            return left.Equals(right);
        }

        public static bool operator !=(Vector2 left, Vector2 right) {
            return !left.Equals(right);
        }
        #endregion

        #region static methods
        public static Vector2 Normalize(Vector2 v) {
            Normalize(ref v, out v);
            return v;
        }
        public static void Normalize(ref Vector2 v, out Vector2 o) {
            float scale = 1.0f / v.Length;
            o.X = v.X * scale;
            o.Y = v.Y * scale;
        }

        public static float Dot(Vector2 left, Vector2 right) {
            float r;
            Dot(ref left, ref right, out r);
            return r;
        }

        public static void Dot(ref Vector2 left, ref Vector2 right, out float result) {
            result = left.X * right.X + left.Y * right.Y;
        }

        public static Vector2 Reflect(Vector2 vector, Vector2 normal) {
            Vector2 o;
            Reflect(ref vector, ref normal, out o);
            return o;
        }

        public static void Reflect(ref Vector2 vector, ref Vector2 normal, out Vector2 o) {
            float num = (vector.X * normal.X) + (vector.Y * normal.Y);
            o.X = vector.X - ((2f * num) * normal.X);
            o.Y = vector.Y - ((2f * num) * normal.Y);
        }

        public static Vector2 Rotate(Vector2 vector, float degrees) {
            Vector2 v;
            Rotate(ref vector, degrees, out v);
            return v;
        }

        public static void Rotate(ref Vector2 vector, float degrees, out Vector2 o) {
            float radians = MathHelper.ToRadians(degrees);
            o.X = (float)System.Math.Cos(System.Math.Atan2(vector.Y, vector.X) - radians);
            o.Y = (float)System.Math.Sin(System.Math.Atan2(vector.Y, vector.X) - radians);
        }
        #endregion

        #region standard vektoren
        public static readonly Vector2 Down = new Vector2(0, 1);
        public static readonly Vector2 Left = new Vector2(-1, 0);
        public static readonly Vector2 One = new Vector2(1, 1);
        public static readonly Vector2 Right = new Vector2(1, 0);
        public static readonly Vector2 Up = new Vector2(0, -1);
        public static readonly Vector2 Zero = new Vector2();
        #endregion

        public override bool Equals(object obj) {
            if (!(obj is Vector2))
                return false;

            return this.Equals((Vector2)obj);
        }

        public bool Equals(Vector2 other) {
            return
                X == other.X &&
                Y == other.Y;
        }

        public override int GetHashCode() {
            return X.GetHashCode() ^ Y.GetHashCode();
        }
    }
}